package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.CGBLog;
import com.cy.pj.sys.service.CGBLogsService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * 在Spring AOP应用中，基于@Aspect注解描述的
 * 类型为一个切面类型，此类中要封装切入点及通知方法的定义
 * 1)切入点:要切入扩展业务逻辑的一些目标方法集合(例如使用了特点注解描述的方法)
 * 2)通知：封装了扩展业务逻辑的一个方法(spring中也可使用特定注解进行描述)
 */
@Slf4j
@Aspect
@Component
public class CGBLogAspect {
    /**
     * @Pointcut 注解用于定义切入点 这里的  @Pointcut("@annotation为一种切入点表达式
     * 表示由RequiredLog注解描述的方法为一个切入点方法，我们要在这样的方法上添加扩展业务逻辑
     * @Pointcut 注解用于定义切入点方法
     */
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}   //此方法只负责承载切入点的定义
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp)throws Throwable{
        long t1=System.currentTimeMillis();
        log.info("method strat{}", t1);
        try {
            Object result=jp.proceed();//通过连接点调用目标方法
            long t2=System.currentTimeMillis();
            log.info("method end{}", t2);
            //记录用户正常日志
            doSaveLogInfo(jp,(t2-t1),null);//springboot池中的线程
            return result;
        }catch (Throwable e){
            log.error("method error {}",System.currentTimeMillis());
            long t3=System.currentTimeMillis();
            //记录用户异常行为日志
            doSaveLogInfo(jp,(t3-t1),e);
            throw e;
        }
    }

    @Autowired
    private CGBLogsService cgbLogsService;
    public void doSaveLogInfo(ProceedingJoinPoint jp,long time,Throwable e)
            throws NoSuchMethodException, JsonProcessingException {
        //1.获取用户行为日志
        //获取用户登录信息
        String MenuId="admin";//后续登录用户名
        //2.获取登录用户的ip地址
        String ip="192.168.100.11";
        //3.获取用户操作
        //3.1 获取方法所在类的字节码对象
        Class<?> cls=jp.getTarget().getClass();
        //3.2 获取注解描述的方法对象（字节码对象、方法名、参数列表）
        Signature signature=jp.getSignature();
        MethodSignature methodSignature=(MethodSignature) signature;
        Method targetMethod=
                cls.getDeclaredMethod(methodSignature.getName(),methodSignature.getParameterTypes());
        //3.3 获取RequiredLog注解
        RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
        String operation=requiredLog.operation();
        //3.4 获取方法声明信息
        String method=cls.getName()+"."+targetMethod.getName();
        //3.5 获取方法执行时传入的实际参数
        Object[]args=jp.getArgs();//实际参数
        String params=new ObjectMapper().writeValueAsString(args);
        //3.6 获取状态信息
        Integer status=e==null?1:0;//1表示ok
        //3.7 获取错误信息
        String error=e==null?"":e.getMessage();
        //3.8 封装用户行为日志
        CGBLog cgbLog=new CGBLog();
        cgbLog.setCreatedUser("admin");
        cgbLog.setIp(ip);
        cgbLog.setCreatedTime(new Date());
        cgbLog.setOperation(operation);
        cgbLog.setMethod(method);
        cgbLog.setParams(params);
        cgbLog.setStatus(status);
        cgbLog.setError(error);
        cgbLog.setMenuId(MenuId);
        //3.9 将日志写入到数据库
        log.info("user log {}", new ObjectMapper().writeValueAsString(cgbLog));
        cgbLogsService.saveLog(cgbLog);
    }
}
